import traceback, pickle, time
from enum import Enum
from colorama import init, Fore, Back, Style
from datetime import timezone,timedelta,datetime
class ToolKit:
    TIMEZONE_VALUE = 8
    TIMEZONE = timezone(timedelta(hours=+8))
    @staticmethod
    def translateDruation(seconds):
      hours = int(seconds / 3600)
      seconds -= (hours * 3600)
      mins = int(seconds / 60)
      seconds = int(seconds - (mins * 60))
      return "{}:{}:{}".format(hours,mins,seconds)

    @staticmethod
    def getTimeNow(human=False):
        now = datetime.now().astimezone(ToolKit.TIMEZONE)
        if(human):
            now = now.strftime("%Y-%m-%d %H:%M:%S")
        return now

    @staticmethod
    def getSpecialTime(baseDate=None, *, hour=0, minute=0, limitToday=False):
        if(baseDate is None):baseDate = ToolKit.getTimeNow()
        deltaDay = 0
        deltaHour = abs(hour - baseDate.hour)
        deltaMinutes = abs(minute - baseDate.minute)
        if(baseDate.hour > hour):
            if(not limitToday): deltaDay = 1
            deltaHour *= -1
        if(baseDate.minute > minute): deltaMinutes *= -1
        return ToolKit.calcDeltaTime(baseDate, days=deltaDay, hours=deltaHour, minutes=deltaMinutes, seconds=-baseDate.second)

    @staticmethod
    def calcDeltaTime(baseDate, *, days=0, hours=0, minutes=0, seconds=0):
        return baseDate + timedelta(days=days, hours=hours, minutes=minutes, seconds=seconds,microseconds=-baseDate.microsecond)

    @staticmethod
    def calcIntervalSeconds(value):
        now = ToolKit.getTimeNow()
        Log.d("calc interval seconds from:{} to {}".format(now, value))
        return (value - ToolKit.getTimeNow()).total_seconds()

    @staticmethod
    def getPicklingErrors(obj, reslut=None):
        if(result is None):result = {}
        try:
            state = obj.__getstate__()
        except AttributeError:
            return
        if(state is None): return
        if(isinstance(state, tuple)):
            if(not isinstance(state[0], dict)):
                state = state[1]
            else:
                state = state[0].update(state[1])
        for i in state:
            try:
                pickle.dumps(state[i], pickle.HIGHEST_PROTOCOL)
            except Exception as e:
                if(i not in result.keys()):
                    result[i] = str(e)
class Log:
    STREAM = Enum('STREAM', {
        'START':0,
        'KEEP':1,
        'CHANGE':2,
        'STOP':3
    })
    LEVEL = {
        'DEBUG': Enum("DI",{
            'FLAG':'D',
            'FORE':Fore.GREEN,
            'BACK':Back.RESET,
            'STYLE':Style.NORMAL,
            'WEIGHT':0
        }),
        'INFO': Enum("II",{
            'FLAG':'I',
            'FORE':Fore.CYAN,
            'BACK':Back.RESET,
            'STYLE':Style.DIM,
            'WEIGHT':1
        }),
        'WARN': Enum("WI",{
            'FLAG':'W',
            'FORE':Fore.MAGENTA,
            'BACK':Back.RESET,
            'STYLE':Style.BRIGHT,
            'WEIGHT':2
        }),
        'ERROR': Enum("EI",{
            'FLAG':'E',
            'FORE':Fore.YELLOW,
            'BACK':Back.RED,
            'STYLE':Style.BRIGHT,
            'WEIGHT':3
        })
    }
    
    DEF_LEVEL = LEVEL["DEBUG"]
    COLOR_INITED = False

    @staticmethod
    def __printMsg__(msg, level):
        if(not Log.COLOR_INITED):
            init(autoreset=True)
            Log.COLOR_INITED = True
        if(level.WEIGHT.value >= Log.DEF_LEVEL.WEIGHT.value):
            tb = traceback.extract_stack()[-4]
            label = "{} | {} | {}:{}".format(level.FLAG.value, ToolKit.getTimeNow(True), tb[0], tb[1])
            msg = f"{level.FORE.value}{level.STYLE.value}{msg}"
            if(level.BACK is not None):
                msg = f"{level.BACK.value}{msg}"
            print(f"{label} | {msg}", flush=True)
            if(level.WEIGHT.value == Log.LEVEL["ERROR"].WEIGHT.value):
                print("-" * 30)
                print(traceback.format_exc())
                print("-" * 30, flush=True)

    @staticmethod
    def d(msg):
        Log.__printMsg__(msg, Log.LEVLE["DEBUG"])

    @staticmethod
    def i(msg):
        Log.__printMsg__(msg, Log.LEVEL["INFO"])

    @staticmethod
    def w(msg):
        Log.__printMsg__(msg, Log.LEVEL["WARN"])

    @staticmethod
    def e(msg):
        Log.__printMsg__(msg, Log.LEVEL["ERROR"])

    @staticmethod
    def chat(role, phrase, isThinkchain, status:STREAM):
        if(isThinkchain and status == Log.STREAM.START):
            init(autoreset=True)
            timeinfo = "{} {}".format(role, ToolKit.getTimeNow(True))
            print(timeinfo)
            print("-"*len(role))
        elif(isThinkchain and status == Log.STREAM.KEEP):
            level = Log.LEVEL["DEBUG"]
            text = f"{level.FORE.value}{level.STYLE.value}{phrase}"
            if(level.BACK.value):
                text = f"{level.BACK.value}{text}"
            print(text, end='', flush=True)
        elif(not isThinkchain and status ==Log.STREAM.CHANGE):
            print(Style.RESET_ALL)
            print("-"*len(role))
        elif(status == Log.STREAM.STOP):
            print(phrase + "\n")
        else:
            print(phrase, end='', flush=True)
        time.sleep(0.1)
